/*
 * @Author: xiaosihan
 * @Date: 2024-08-15 10:35:24
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2024-08-17 03:22:13
 */

import { isUndefined, uniq } from "lodash";
import cloneDeep from "lodash/cloneDeep";
import { Euler, Group, Mesh, Vector3 } from "three";
import InstancedMeshBase from "three-base/InstancedMeshBase";
import MeshBase from "three-base/MeshBase";
import threeUtils from "three-base/threeUtils";

// 多模型数据结构
export type ManyModelData = {
    id: string,
    name: string,
    url: string,
    interpolation?: number, // 距离上一个点之间的插值距离自动填充等距数据
    position: {
        x: number,
        y: number,
        z: number,
    },
    rotation?: {
        x: number,
        y: number,
        z: number,
    },
    scale?: {
        x: number,
        y: number,
        z: number,
    },
}

// 大量的模型组对象
// 用于处理大量的重复的模型
export default class ManyModelGroup extends Group {
    declare children: InstancedMeshBase[];

    //根据url 获取实例模型组, 没有就创建一个新的
    getInstanceMeshGroupByUrl(url: string): InstancedMeshBase {
        let instanceMeshGroup = this.children.find(o => o.url === url);
        if (!instanceMeshGroup) {
            instanceMeshGroup = new InstancedMeshBase(url);
            instanceMeshGroup.addEventListener("loaded", () => {
                const mesh = new MeshBase(url);
                mesh.addEventListener("loaded", () => {
                    threeUtils.center(mesh);
                    threeUtils.alignBottom(mesh, 0.1);
                    threeUtils.applyAllTranstion(mesh);
                });
            });
            instanceMeshGroup.castShadow = true;
            instanceMeshGroup.receiveShadow = true;
            instanceMeshGroup.frustumCulled = false;
            this.add(instanceMeshGroup);
        }
        return instanceMeshGroup;
    }

    // 保存数据
    datas: Array<ManyModelData> = [];
    async setDatas(datas: Array<ManyModelData> = []) {
        this.datas = [...datas];
        //按照url 分组
        const urls = uniq(datas.map(d => d.url));
        //显示有效的模型组
        this.children.map(o => o.visible = urls.includes(o.url));

        // 遍历 url 获取相同的url的数据
        for (let url of urls) {
            const modelDatasWithUrl = datas.filter(d => d.url === url);
            // 按照 url来获取 InstanceMeshGroup 对象
            const instanceMeshGroup = this.getInstanceMeshGroupByUrl(url);

            let conut = 0; //计算数据总数
            for (let i = 0; i < modelDatasWithUrl.length; i++) {
                let pre = modelDatasWithUrl[i - 1];
                let { position, interpolation } = modelDatasWithUrl[i];
                if (
                    !isUndefined(pre) &&
                    !isUndefined(position) &&
                    !isUndefined(interpolation)
                ) {
                    const distance = new Vector3(
                        position.x - pre.position.x,
                        position.y - pre.position.y,
                        position.z - pre.position.z
                    ).length();

                    const num = Math.ceil(distance / interpolation);

                    conut += num;
                } else {
                    conut += 1;
                }

            }

            // 设置实例数量
            await instanceMeshGroup.setCount(conut);
            let instanceIndex = -1;
            for (let i = 0; i < modelDatasWithUrl.length; i++) {
                const { position, rotation, scale, interpolation } = modelDatasWithUrl[i];

                const interpolationData: Array<ManyModelData> = [];

                let pre = modelDatasWithUrl[i - 1];
                if (
                    !isUndefined(pre) &&
                    !isUndefined(position) &&
                    !isUndefined(interpolation)
                ) {
                    const distance = new Vector3(
                        position.x - pre.position.x,
                        position.y - pre.position.y,
                        position.z - pre.position.z
                    ).length();

                    const num = Math.ceil(distance / interpolation);
                    const step = distance / num; // 计算平均间距

                    for (let ii = 1; ii < (num + 1); ii++) {

                        let startP = new Vector3(pre.position.x, pre.position.y, pre.position.z);
                        let direction = new Vector3(
                            position.x - pre.position.x,
                            position.y - pre.position.y,
                            position.z - pre.position.z
                        ).setLength(ii * step);

                        interpolationData.push({
                            ...modelDatasWithUrl[i],
                            position: {
                                x: startP.x + direction.x,
                                y: startP.y + direction.y,
                                z: startP.z + direction.z,
                            }
                        });

                    }

                } else {
                    interpolationData.push({ ...modelDatasWithUrl[i] });
                }

                //遍历插值数据
                for (let iii = 0; iii < interpolationData.length; iii++) {
                    instanceIndex++;
                    const { position: p, rotation: r, scale: s } = interpolationData[iii];

                    if (p) {
                        instanceMeshGroup.setTRS(instanceIndex, { position: new Vector3(p.x, p.y, p.z) });
                    }
                    if (r) {
                        instanceMeshGroup.setTRS(instanceIndex, { rotation: new Euler(r.x, r.y, r.z) });
                    }
                    if (s) {
                        instanceMeshGroup.setTRS(instanceIndex, { scale: new Vector3(s.x, s.y, s.z) });
                    }
                }


            }

        }

    }
    getDatas() {
        return cloneDeep(this.datas);
    }
    addData(data: ManyModelData) {
        this.setDatas([...this.datas, data]);
    }
    delLatest() {
        this.datas.pop()
        this.setDatas([...this.datas]);
    }
    delData(id: string) {
        this.setDatas([...this.datas.filter(d => d.id !== id)]);
    }
    updataData(id: string, data: Partial<Omit<ManyModelData, "id">>) {
        this.datas.map(d => {
            if (d.id === id) {
                Object.assign(data, d);
            }
        });
        this.setDatas(this.datas);
    }
    // 下载数据
    downloadData() {
        const text = JSON.stringify(this.datas, null, 1);
        const blob = new Blob([text], { type: 'text/plain' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = "manydata.json";
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }

}